﻿/*************************************************
taurus核心处理类
author：Daixiwei
**************************************************/
using System;
using System.Collections.Generic;

namespace taurus.client
{
    public class SocketEngine : ISocketLayer
    {
        internal ISocket _socket;
        internal ISocket _udpSocket;

        private string _sessionToken;
        private string _serverAddress;
        internal int protocolCompression = 100;

        private Action<SocketCode> _onConnect;
        internal Action<Message> _onEvent;
        readonly Queue<Action> _actionQueue = new Queue<Action>();
        internal LagMonitor _lagMonitor;
        internal SystemController _controller;
        internal IOHandler _packetHandler;
        private TaurusClient _client;
        internal int sessionId;
        private ITObject _validate;

        public SocketEngine(TaurusClient client)
        {
            _socket = new SocketTcp(this);
            if (client.getUdpPort() > 0)
            {
                _udpSocket = new SocketUdp(this,client.getUdpPort());
            }
            
            _controller = new SystemController(this);
            _packetHandler = new IOHandler(this, _controller);
            _lagMonitor = new LagMonitor(this, NetManager.WriteTimeOut, 10);
            _client = client;
        }

        public void connect()
        {
            _validate = TObject.newInstance();
            _serverAddress = _client.getHost();
            _socket.connect();
        }

        public void connect(ITObject validate)
        {
            _validate = validate;
            _serverAddress = _client.getHost();
            _socket.connect();
        }

        public void disconnect()
        {
            if (_udpSocket != null) _udpSocket.disconnect();
            _socket.disconnect();
            if (_lagMonitor != null) _lagMonitor.destroy();
            _lagMonitor = null;
        }

        public void processEvent()
        {
            if (_socket.isConnected() && _controller.isTimeOut())
            {
                _onConnect(SocketCode.TimeoutDisconnect);
                return;
            }

            while (true)
            {
                Action myAction;
                lock (_actionQueue)
                {
                    if (_actionQueue.Count <= 0)
                    {
                        break;
                    }
                    myAction = _actionQueue.Dequeue();
                }
                myAction();

            }
        }

        void ISocketLayer.write(Message msg)
        {
            _controller.sendRequest(msg);
        }

        internal void onStatusChanged(SocketCode statusCode)
        {
            if(statusCode == SocketCode.UDPConnect)
            {
                onConnect();
                return;
            }
            if (statusCode != SocketCode.Connect)
            {
                this.disconnect();
            }
            Logger.info("status : " + statusCode);
            if (statusCode == SocketCode.Connect)
            {
                onConnect();
            }
            else
            {
                if (_onConnect != null)
                    _onConnect(statusCode);
            } 
        }

        void onConnect()
        {
            _controller.refreshTime();
            _lagMonitor.start();
            if (_onConnect != null)
                _onConnect(SocketCode.Connect);
        }

        internal void sendValidate()
        {
            _controller.sendValidate(_validate);
        }

        internal void onValidate()
        {
            if (this._udpSocket != null)
            {
                _udpSocket.connect();
            }
            else
            {
                enqueueStatusCallback(SocketCode.Connect);
            }
        }



        internal void enqueueActionForDispatch(Action action)
        {
            lock (_actionQueue)
            {
                _actionQueue.Enqueue(action);
            }
        }

        internal void enqueueStatusCallback(SocketCode statusValue)
        {
            lock (_actionQueue)
            {
                _actionQueue.Enqueue(delegate
                {
                    onStatusChanged(statusValue);
                });
            }
        }

        public string getServerAddress()
        {
            return _serverAddress;
        }

        public ConnectionProtocol getProtocol()
        {
            return ConnectionProtocol.Tcp;
        }

        public void setOnConnect(Action<SocketCode> onConnect)
        {
            this._onConnect = onConnect;
        }

        public void setOnEvent(Action<Message> onEvent)
        {
            this._onEvent = onEvent;
        }

        public int getAveragePingTime()
        {
            return _lagMonitor != null ? _lagMonitor.getAveragePingTime() : 300;
        }

        public string getSession()
        {
            return this._sessionToken;
        }

        public void setSession(string session)
        {
            this._sessionToken = session;
        }

        public void clearResponse()
        {
            Logger.warn("TCP Unsupported!");
        }
    }
}
